﻿// MIT License (MIT)
// 
// Copyright (c) 2014 K Ercan Turkarslan
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// 

using System;
using System.Text;
using System.IO;

namespace CodeGen
{
    public class CodeBlock
    {
        private Int32 _indent;

        private readonly StringBuilder _sb;

        #region Private General Methods

        private String Indent(Int32 indent)
        {
            return "".PadRight(indent*4, ' ');
        }

        #endregion

        #region Code Segment Methods

        public void Begin_Block()
        {
            _sb.AppendFormat("{0}{{\r\n", Indent(_indent));
            IncreaseIndent();
        }

        public void Begin_Get()
        {
            _sb.AppendFormat("{0}get\r\n{0}{{\r\n", Indent(_indent));
            IncreaseIndent();
        }

        public void CheckNullParameter(String paramName)
        {
            Insert(String.Format("if ({0} == null)", paramName));
            ThrowException("ArgumentNullException", paramName);
            NewLine();
        }

        public void CheckNullOrEmptyStringParameter(String paramName)
        {
            Insert(String.Format("if (String.IsNullOrEmpty({0}))", paramName));
            ThrowException("ArgumentNullException", paramName);
            NewLine();
        }

        public void ChildClass(String name, String parent)
        {
            _sb.AppendFormat("{0}public class {1} : {2}\r\n{0}{{\r\n", Indent(_indent), name, parent);
            IncreaseIndent();
        }

        public void Class_Begin(String accessor, String name)
        {
            Insert(String.Format("{0} class {1}", accessor, name));
            Begin_Block();
        }

        public void Class_End()
        {
            End_Block("End of class");
        }

        public void Comment(params String[] comments)
        {
            foreach (var comment in comments)
            {
                _sb.AppendFormat("{0}// {1}\r\n", Indent(_indent), comment);
            }
            NewLine();
        }

        public void CommentSingle(params String[] comments)
        {
            _sb.AppendFormat("{0}// ", Indent(_indent));

            foreach (var comment in comments)
            {
                _sb.AppendFormat("{0} ", comment);
            }

            _sb.Append("\r\n");
        }

        public void Constructor_Begin(String accessor, String name, String parameters = null, String baseCall = null)
        {
            Insert(String.Format("{0} {1}({2})", accessor, name, parameters));

            if(baseCall != null)
                InsertIndented(String.Format(": {0}", baseCall));       
     
            Begin_Block();
        }

        public void Constructor_End()
        {
            End_Block("End Constructor");
        }

        public void End_Block()
        {
            DecreaseIndent();
            _sb.AppendFormat("{0}}}\r\n", Indent(_indent));
        }

        public void End_Block(String comment)
        {
            DecreaseIndent();
            _sb.AppendFormat("{0}}} //{1}\r\n", Indent(_indent), comment);
        }

        public void End_Get()
        {
            DecreaseIndent();
            _sb.AppendFormat("{0}}}\r\n", Indent(_indent));
        }

        public void If(String expression)
        {
            _sb.AppendFormat("{0}if({1})\r\n", Indent(_indent), expression);
        }

        public void Action_Begin(String methodName, String parameters, String returnType = null)
        {
            Region_Begin(String.Format("Action {0}({1}){2}", methodName, parameters, returnType == null ? "" : String.Format(" : {0};", returnType)));
            Insert(String.Format("public {2} {0}({1})", methodName, parameters, returnType ?? "void"));
            Begin_Block();
        }

        public void Action_End(String methodName)
        {
            End_Block();
            Region_End(String.Format("Action {0}", methodName));
        }

        public void Method_Begin(String accessor, String methodName, String parameters, String returnType = null)
        {
            Region_Begin(String.Format("Method {0}({1}){2}", methodName, parameters, returnType == null ? "" : String.Format(" : {0};", returnType)));
            Insert(String.Format("{3} {2} {0}({1})", methodName, parameters, returnType ?? "void", accessor ?? "public"));
            Begin_Block();
        }

        public void Method_End(String methodName)
        {
            End_Block();
            Region_End(String.Format("Method {0}", methodName));
        }

        private void MIT_License(String fullname, String year)
        {
            Comment(
                "MIT License (MIT)",
                "",
                String.Format("Copyright (c) {1} {0}", fullname, year),
                "",
                "Permission is hereby granted, free of charge, to any person obtaining a copy",
                "of this software and associated documentation files (the \"Software\"), to deal",
                "in the Software without restriction, including without limitation the rights",
                "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
                "copies of the Software, and to permit persons to whom the Software is",
                "furnished to do so, subject to the following conditions:",
                "",
                "The above copyright notice and this permission notice shall be included in all",
                "copies or substantial portions of the Software.",
                "",
                "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
                "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
                "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
                "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
                "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
                "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE",
                "SOFTWARE.",
                "");
        }

        public void Namespace_Begin(String name)
        {
            _sb.AppendFormat("namespace {0}\r\n", name);
            Begin_Block();
        }

        public void Namespace_End()
        {
            End_Block("End of Namespace");
        }

        public void NewLine()
        {
            _sb.AppendFormat("{0}\r\n", Indent(_indent));
        }

        public void PrivateMember(String type, String name)
        {
            Insert(String.Format("private {0} _m{1};", type, name));            
        }

        public void Region_Begin(String text)
        {
            _sb.AppendFormat("{0}#region {1}\r\n", Indent(_indent), text);
        }

        public void Region_End(String comment = "")
        {
            _sb.AppendFormat("{0}#endregion{1}\r\n", Indent(_indent), String.IsNullOrEmpty(comment) ? "" : String.Format(" {0}", comment));
            NewLine();
        }

        public void Switch_Begin(String expression)
        {
            Insert(String.Format("switch({0})", expression));
            Begin_Block();
        }

        public void Switch_Case(String value)
        {
            Insert(String.Format("case \"{0}\":", value));
        }

        public void Switch_Case_Break()
        {
            InsertIndented("break;");
        }

        public void Switch_Case_Return(String returnExpression)
        {
            InsertIndented(String.Format("return {0};", returnExpression));
        }

        public void Switch_Default()
        {
            Insert("default:");
        }

        public void Switch_End()
        {
            End_Block();
        }

        public void ThrowException(String exName, String exMessage)
        {
            InsertIndented(String.Format("throw new {0}(\"{1}\");", exName, exMessage));
        }

        public void Using(String reference)
        {
            Insert(String.Format("using {0};", reference));
        }

        #endregion

        #region Code Insertion Methods

        public void Insert(String code)
        {
            _sb.AppendFormat("{0}{1}\r\n", Indent(_indent), code);
        }

        public void InsertIndented(String code)
        {
            InsertIndented(code, 1);
        }

        private void InsertIndented(String code, Int32 indentCount)
        {
            _sb.AppendFormat("{0}{1}\r\n", Indent(_indent + indentCount), code);
        }

        private void IncreaseIndent()
        {
            _indent++;
        }

        private void DecreaseIndent()
        {
            if (_indent > 0)
                _indent--;
        }

        #endregion

        #region CamelCase Method

        public static String CamelCase(String value)
        {
            return value.Substring(0, 1).ToLower() + value.Substring(1);
        }

        #endregion


        #region WriteToFile Method

        public void WriteToFile(String fullFilename)
        {
            if (File.Exists(fullFilename))
                throw new InvalidOperationException("Can't override existing file!");

            using (var outF = new StreamWriter(fullFilename, false))
            {
                outF.WriteLine(_sb.ToString());
            }
        }
            
        #endregion

        public static CodeBlock NewCodeFile(String fullname, String year)
        {
            var cb = new CodeBlock();

            cb.Comment(
                "AUTO GENERATED CODE!",
                "CODE GENERATOR WILL NOT PRESERVE MANUAL CHANGES TO THIS CODE!",
                "");

            cb.MIT_License(fullname, year);
            cb.NewLine();

            return cb;
        }

        #region Constructor

        private CodeBlock()
        {
            _sb = new StringBuilder();
            _indent = 0;
        }

        #endregion
    }
}
